Cloud Connected

Thoughts and Ideas from the Gitana Development Team

* Showing search results for tag: Access Policies

Enterprise Access Policies in Gitana 4.0 (Part 2)

In this article, we'll continue our series on Access Policies within Gitana. We'll take a few interesting and more complex examples of how Access Policies may be used to guarantee compliance witih enterprise security requirements.

In the previous article, we left off looking at how you can grant access to content using Access Policies that are defined at either a Platform or Project-level. Platform-scoped policies can be written once and applied across all of your Projects. In this way, you can centrally manage Access Policies that are applied to Projects using regular expressions on the Project ID, title or a custom field.

Alternatively, you can assign them one-by-one to your Projects. Or allow Project Managers to manage their own Access Policies from within their Projects.

Let us extend on this and look at some new Access Policies that do interesting things!

Grant the Consumer Role to Specific Folders

Suppose you have a single Project. You've invited a vendor to the Project and you'd like to allow that vendor's users to be able to view a specific folder and all of its content. Suppose the vendor is ACME Corporation and the path to their resources is /resources/acme.

Let's use the path condition to define an Access Policy that does just this!

{
    "title": "Consumer of Content in Folder /resources/acme",
    "statements": [{
        "action": "grant",
        "roles": ["consumer"],
        "conditions": [{
            "type": "path",
            "config": {
                "path": "/resources/acme(.*)"
            }
        }]
    }]
}

That's it. We assign this Access Policy to the ACME team or group. And by doing so, all members of that team or group will be able to view the /resources/acme folder. It also means that those members will be able to perform full-text searches, GraphQL queries or any other API call within Gitana and read the content contained within that folder.

What if we wanted to define this Access Policy at the platform level and have it apply only to specific Projects? We can borrow the statements from the previous article's Access Policy and combine them here to build a multi-statement Access Policy that runs at the Platform level and can be used across multiple Projects!

{
    "title": "Consumer of Content in Folder /resources/acme",
    "statements": [{
        "action": "grant",
        "roles": ["consumer"],
        "conditions": [{
            "type": "path",
            "config": {
                "path": "/resources/acme(.*)"
            }
        }, {
            "type": "project",
            "config": {
                "property": "has_vendor_resources",
                "value": true
            }
        }]
    }]
}

In this way, this single Access Policy will work across multiple Projects. Specifically, it will work across Projects that have has_vendor_resources set to true.

It's worth noting that you can also limit the grant to the specific folder -without- granting for subfolder elements. For example, if you wanted folks to be able to see the acme folder but -not- see any sub-items, you can change the path to:

"path": "/resources/acme"

You can learn more about the path condition here:
https://gitana.io/documentation/gitana/4.0/engine/security/access-policies/conditions/path.html

Grant Consumer Role to Content on a Specific Branch

Gitana is unique in that it is one of the only content platforms that offers Git-like branches and versioning. Every change that your editorial team makes is captured. And editorial teams are empowered to work in branched workspaces, allowing them to make changes at the same time without stepping on each other's toes!

Your Access Policies can be scoped to work against all branches or against specific branches. The branch condition allows you to scope your Access Policy statements to branches as you please.

Here is an example where the Consumer role is granted to all nodes on the branch named My Workspace.

{
    "title": "Consumer of Content on the 'My Workspace' branch",
    "statements": [{
        "action": "grant",
        "roles": ["consumer"],
        "conditions": [{
            "type": "branch",
            "config": {
                "title": "My Workspace"
            }
        }]
    }]
}

We can also grant by Branch ID or by Branch alias. Here is a statement which uses an $or composite clause to allow for two cases:

{
    "title": "Consumer of Content on Two Branches",
    "statements": [{
        "action": "grant",
        "roles": ["consumer"],
        "conditions": [{
            "type": "or",
            "config": {
                "conditions": [{
                    "type": "branch",
                    "config": {
                        "id": "574118a48a2e68c897ed"
                    }
                }, {
                    "type": "branch",
                    "config": {
                        "alias": "authoring"
                    }
                }]
            }
        }]
    }]
}

Finally, we can also bind the Access Policies to any other property on a Branch (including custom properties) using the property and value options. Suppose our team of editors is kicking off branches left and right for various purposes. We can write our Access Policy to apply to those branches by simply saving them with a custom property. If each Branch had a property purpose with a value of daily_update, we could do something like this:

{
    "title": "Consumer of Content on Daily Update branches",
    "statements": [{
        "action": "grant",
        "roles": ["consumer"],
        "conditions": [{
            "type": "branch",
            "config": {
                "property": "purpose",
                "value": "daily_update"
            }
        }]
    }]
}

We can assign this Access Policy at the Project level to have it apply to all resources within a specific-Project. Or we can define it at the Platform level to have it work across all Projects. If we did that, we'd likely want to introduce additional conditions to selectively decide which Projects the Access Policy should apply to.

You can learn more about the branch condition here:
https://gitana.io/documentation/gitana/4.0/engine/security/access-policies/conditions/branch.html

Grant Consumer Role to Content by Node Type QName

Gitana provides a content modeling facility that lets you define the structure of your content types, content associations and features/aspects using JSON Schema. Everything is compiled at a branch-level, on-the-fly, as you make changes. Compile-time validators run to ensure that your content model is always consistent and valid. The system offers complete support for hierarchical inheritance, property-level overridess and even aspect-oriented feature injection against both content definitions and instances.

Your Access Policies are further able to scope their bindings to specific content definitions in your model. In this way, you can grant or revoke authorities against specific content types, letting you assign permission for teams to work on specific types or lineages of content in one fell swoop.

Suppose you wanted to allow Bill, Tina and Joe the ability to edit Press Releases. Your Press Releases might be implemented as a content type with the type QName of my:press-release. You could use the following Access Policy to grant the Editor authority:

{
    "title": "Editor of Press Releases",
    "statements": [{
        "action": "grant",
        "roles": ["editor"],
        "conditions": [{
            "type": "type-qname",
            "config": {
                "qname": "my:press-release"
            }
        }]
    }]
}

You would then put Bill, Tina and Joe onto a "Press Release Editors" team. Assign this Acccess Policy to the team and away you go!

You can learn more about the type-qname condition here:
https://gitana.io/documentation/gitana/4.0/engine/security/access-policies/conditions/type-qname.html

Grant Consumer Role to Content tagged with a Feature / Aspect

Gitana comes preloaded with a large number of features / aspects that you can use to inject important metadata and important behaviors onto your content instances and content types. Features are aspects that let you define concerns (behaviors / metadata) that you can snap into your content hierarchy from the side.

This provides a valuable tool that works seamlessly alongside hierarchical modeling! It allows you to snap in properties and behaviors that are centrally-managed. This injection effectively comes in from the side, instead of hierarchically, and lets you target any content definition within the tree.

For example, Gitana comes with a feature called f:watermarkable that can be marked onto a node to have that node's image attachment automatically undergo watermarking. That is to say, the node's original image has a watermark applied to it and the resulting image is added to the node as a new attachment.

Suppose you wanted to write a policy that granted Editor rights to all watermarked files. That's now very easy. You can do so like this:

{
    "title": "Editor of Watermarked Files",
    "statements": [{
        "action": "grant",
        "roles": ["editor"],
        "conditions": [{
            "type": "feature",
            "config": {
                "qname": "f:watermarkable"
            }
        }]
    }]
}

That's it. Assign this policy to a user to allow that user to edit all of the nodes with watermarked images in the system!

You can learn more about the feature condition here:
https://gitana.io/documentation/gitana/4.0/engine/security/access-policies/conditions/feature.html

Grant Editor Role to Translators for Localized Content

In Gitana, you use the f:multilingual feature to mark an individual content instance as supporting one or more multilingual translatons. Or, you can mark an entire Content Type within the dictonary so that all content instances of that type (or inherited types) will support multilingual translations.

Multilingual translations are stored within the content graph as associated nodes that contain the localized key/value pairs and the localized binary attachments for a given locale. Each associated node stores the localized data for a single translation. There is also no limit to the number of translations that a multlingual node may support!

You can modify individual translations manually or you can utilize our integrated AI services to automatically produce translated values for your properties. The central node in the graph structure is known as the "master node". You can set things up so that any changes to this master node will cause your localized content automatically translate using these integrated AI services.

Regardless of your approach, it is often the case that organizations will want to delegate either the editing of translations or the approval of automated translations to one or more translation teams. There may be one team of editors whose responsibility it is to manage all localized content. Or you may, for example, have multiple teams where one team is dedicated to Spanish content, another to German content and another to Chinese content.

Access Policies allow you to limit access rights to content based on the locale of the content. You can configure things so that everyone can see each other's content and translations while limiting which teams are specifically responsible for editing or approving the localized translations themselves.

Here is an Access Policy that grants Editor rights to content that has been localized to Spanish (where f:locale = es).

{
    "title": "Editor of Spanish Translations",
    "statements": [{
        "action": "grant",
        "roles": ["editor"],
        "conditions": [{
            "type": "locale",
            "config": {
                "locale": "es"
            }
        }]
    }]
}

And here is an Access Policy that does the same for Chinese (f:locale = zh).

{
    "title": "Editor of Chinese Translations",
    "statements": [{
        "action": "grant",
        "roles": ["editor"],
        "conditions": [{
            "type": "locale",
            "config": {
                "locale": "zh"
            }
        }]
    }]
}

You can learn more about the locale condition here:
https://gitana.io/documentation/gitana/4.0/engine/security/access-policies/conditions/locale.html

Learn More about Access Policies

This article and its predecessor really only scratch the surface regarding what is possible with Access Policies. We hope you've found it interesting! To learn more about Gitana, Access Policies and Content Modeling, please check out:

  • https://gitana.io
  • https://gitana.io/documentation/gitana/4.0/engine/content-models.html
  • https://gitana.io/documentation/gitana/4.0/engine/security/access-policies.html

Enterprise Access Policies in Gitana 4.0 (Part 1)

In this article, we'll take a look at Access Policies -- a powerful, new feature in Gitana 4.0 that allows organizations to set up and guarantee compliance with complex, enterprise-wide security requirements. Access Policies build upon the existing access control facilities provided which include per-object ACLs and broader, team-based ACLs. They extend those capabilities by allowing administrators to express access rights in a broad sweeping and prescriptive manner -- one that allows for customization and rule-based configuration, defining access constraints that span your entire platform, go across projects, across users and across all of your content via centralized and managed policy control documents.

The Access Policy

An Access Policy document is a JSON document that defines one or more conditional statements. Each statement conditionally grants or revokes one or more authorities against a resource. If effect, each statement is a rule that is evaluated for the current user against the given resource and a determination is made of what authorities to bestow.

The basic structure of an Access Policy document looks like this:

{
    "title": "{title}",
    "statements": [{
        "action": "{action}",
        "roles": [],
        "conditions": [{
            "type": "{conditionType},
            "config": {
                ...
            }
        ]}
    }]
}

Access Policies can be assigned to Users, Groups and Teams. By assigning an Access Policy to a Group or a Team, all members of that group or team will receive the grants or revocations prescribed by the Access Policy. This includes sub-groups or principals members with nested memberships.

Finally, Access Policies can be assigned at multiple scopes. A platform-scoped Access Policy will be evaluated universally across all of your Gitana resources (including within Projects). Where as a project-scoped Access Policy is applicable only to the resources of a specific project.

Using scopes, you can centrally manage Access Policies that span the entire organization as well as centrally manage Access Policies that are then assigned project-by-project to fine tune access control rights within any given project. You may also wish to permit Project Administrators to have rights to further extend Access Policies within their individual projects or constrain that very ability, depending on your specific needs.

Access Policies in Practice

We've pulled together a few examples on how Access Policies are most commonly put to use across our customer base. We've also compiled a few simple examples to help demonstrate what Access Policies are all about.

Let's take a look at a few examples, starting with the simplest of cases.

Grant the Consumer Role to Everything

You can write an unconditional Access policy by simply omitting the conditions array.
This Access policy will grant the Consumer role to all resources.

{
    "title": "Consumer of all Resources",
    "statements": [{
        "action": "grant",
        "roles": ["consumer"]
    }]
}

Notes:

  • If this Access Policy were assigned at the Platform scope, it would grant Consumer rights to a principal for everything on the tenant platform.
  • If it were assigned to a specific Project, it would grant Consumer rights to a principal for everything within the project.

This includes the Project itself, the Stack, the Master Branch, any other Branch and all content on all Branches.

Needless to say, this kind of Access Policy isn't typically used. It's provided here as a starting point. Let's see some more!

Grant the Consumer Role to Content Nodes

We can improve on the previous example by constraining the grant to content nodes only. To do so, we can introduce a Condition. Specifically, we'll use the type condition to constrain the statement to apply only to resources of type node.

{
    "title": "Consumer of all Content Nodes",
    "statements": [{
        "action": "grant",
        "roles": ["consumer"],
        "conditions": [{
            "type": "type",
            "config": {
                "type": "node"
            }
        }]
    }]
}

Notes:

  • If this Access Policy were assigned at the Platform scope, it would grant Consumer rights to a principal for all content nodes across all projects on the tenant platform.
  • If it were assigned to a specific Project, it would grant Consumer rights to a principal for all content nodes within that specific project.

You can learn more about the type condition here:
https://gitana.io/documentation/gitana/4.0/engine/security/access-policies/conditions/type.html

Grant the Consumer Role to Content Nodes in specific Projects

Suppose we wanted to apply the previous example at the Platform level but have it only apply to specific projects. We can use the or condition coupled with the project condition to build out the boolean logic for the binding.

Imagine that we have two projects that host content for "level 4" vendors. The first Project might be named FedEx and the second project might be named UPS. We can constrain our Access Policy to those two projects by Title like this:

{
    "title": "Consumer of all Content Nodes for Level 4 Vendor Projects",
    "statements": [{
        "action": "grant",
        "roles": ["consumer"],
        "conditions": [{
            "type": "type",
            "config": {
                "type": "node"
            }
        }, {
            "type": "or",
            "config": {
                "conditions": [{
                    "type": "project",
                    "config": {
                        "title": "FedEx"
                    }
                }, {
                    "type": "project",
                    "config": {
                        "title": "UPS"
                    }
                }]
            }
        }]
    }]
}

Note that the conditions array elements have an implicit and amongst them.

This Access Policy would be assigned at the Platform level and would only evaluate against resources within the FedEx or UPS projects.

Since the titles of Project can potentially change, we could improve this by sle also elect to constrain by ID, like this:

{
    "title": "Consumer of all Content Nodes for Level 4 Vendor Projects",
    "statements": [{
        "action": "grant",
        "roles": ["consumer"],
        "conditions": [{
            "type": "type",
            "config": {
                "type": "node"
            }
        }, {
            "type": "or",
            "config": {
                "conditions": [{
                    "type": "project",
                    "config": {
                        "id": "db247ccd3539f5aa0ef8"
                    }
                }, {
                    "type": "project",
                    "config": {
                        "id": "ada21783fc8761831bbb"
                    }
                }]
            }
        }]
    }]
}

That's not bad. However, we can improve this even further.

Suppose we simply tagged Projects with a property called vendor_level which identified the level of subscription. Then we could just tag the UPS and FedEx projects with vendor_level set to 4.

Now our Access Policy can be simplified:

{
    "title": "Consumer of all Content Nodes for Level 4 Vendor Projects",
    "statements": [{
        "action": "grant",
        "roles": ["consumer"],
        "conditions": [{
            "type": "type",
            "config": {
                "type": "node"
            }
        }, {
            "type": "project",
            "config": {
                "property": "vendor_level",
                "value": 4
            }
        }]
    }]
}

Much better! This Access Policy will automatically be applied to any Projects with the designated vendor_level. This means we can add, remove and update Projects as we wish and the Access Policy system will dynamically apply our provisioned access rights as we go!

It's also worth noting that any and all conditional rules will perform Regular Expression matching whenever textual content is being compared. In the examples above, we used simple text matches. But we could also imagine a more contrived example where we might want this Access Policy to apply to all Projects that start with the letter A. You could adjust the Condition like this:

{
    "type": "project",
    "config": {
        "title": "^A.*"
    }
}

That's it.

You can use RegEx expressions anyplace where text conditions are concerned. Use them for multi-value matches, prefix or suffix matches, partial matches and much more!

You can learn more about the project condition here:
https://gitana.io/documentation/gitana/4.0/engine/security/access-policies/conditions/project.html

You can learn more about the or condition here:
https://gitana.io/documentation/gitana/4.0/engine/security/access-policies/conditions/or.html

You can learn more about the and condition here:
https://gitana.io/documentation/gitana/4.0/engine/security/access-policies/conditions/and.html

More to Come!

In the next article, we'll continue looking at more complex and interesting examples of Access Policies. We'll take a look at revokes and ordering. And we'll cover examples that employ conditions around Branches and further Content properties including locale, path and aspect-injected, nested property paths!